import Mota from "@/mota";
import { Keyboard } from "@/mota/data/KeyDataUtils";

export type Point = [number, number];

export interface KeyInterface<CHILDREN extends KeyInterface<CHILDREN>> {
    Children?: CHILDREN[] | undefined;
    JudgeKey?: (key: KeyUtils) => void;
    click?: (pos: Point, posf: Point) => void;
    mousemove?: (pos: Point, posf: Point) => void;
    mousedown?: (pos: Point, posf: Point) => void;
    mouseup?: (pos: Point, posf: Point) => void;
    mousewheel?: (delta: number) => void;
};

export default class KeyUtils {
    public readonly Mota: Mota;

    public pos: [number, number] = [-1, -1];

    public posf: [number, number] = [-1, -1];

    public keyMap: Set<Keyboard> = new Set();

    public constructor(mota: Mota) {
        this.Mota = mota;
        this.initEvent();
    }

    public get keyHistory() {
        return Array.from(this.keyMap).reverse();
    }

    public has(key: Keyboard): boolean {
        return this.keyMap.has(key);
    }

    public delete(key: Keyboard): void {
        this.keyMap.delete(key);
    }

    public initEvent() {
        const { eventRemove, draw: { canvas } } = this.Mota;
        const onkeydown = ({ key }: KeyboardEvent) => { this.keyMap.add(<Keyboard>key); };
        window.addEventListener("keydown", onkeydown);
        eventRemove.Add(() => window.removeEventListener("keydown", onkeydown));
        const onkeyup = ({ key }: KeyboardEvent) => { this.keyMap.delete(<Keyboard>key); };
        window.addEventListener("keyup", onkeyup);
        eventRemove.Add(() => window.removeEventListener("keyup", onkeyup));

        const click = (e: MouseEvent) => { this.parsePos(e); this.click(); };
        canvas.addEventListener("click", click);
        eventRemove.Add(() => canvas.removeEventListener("click", click));

        const mousemove = (e: MouseEvent) => { this.parsePos(e); this.mousemove(); };
        canvas.addEventListener("mousemove", mousemove);
        eventRemove.Add(() => canvas.removeEventListener("mousemove", mousemove));

        const mousedown = (e: MouseEvent) => { this.parsePos(e); this.mousedown(); };
        canvas.addEventListener("mousedown", mousedown);
        eventRemove.Add(() => canvas.removeEventListener("mousedown", mousedown));

        const mouseup = (e: MouseEvent) => { this.parsePos(e); this.mouseup(); };
        canvas.addEventListener("mouseup", mouseup);
        eventRemove.Add(() => canvas.removeEventListener("mouseup", mouseup));

        const mousewheel = (e: Event) => { this.mousewheel((<WheelEvent>e).deltaY); };
        canvas.addEventListener("mousewheel", mousewheel);
        eventRemove.Add(() => canvas.removeEventListener("mousewheel", mousewheel));
    }

    public parsePos({ offsetX: cx, offsetY: cy }: MouseEvent) {
        const { Mota: { draw: { x, y, size: { Value: size } } } } = this;
        const txf = (cx - x) / size;
        const tyf = (cy - y) / size;
        const tx = Math.floor(txf);
        const ty = Math.floor(tyf);
        this.pos = [tx, ty];
        this.posf = [txf, tyf];
    }

    public JudgeKeyAll(): void {
        const { Mota } = this
        Mota.ForChildren((info) => {
            if (info.JudgeKey) {
                info.JudgeKey(this);
            }
        });
    }

    public click() {
        const { Mota, pos, posf } = this
        Mota.ForChildren((info) => {
            if (info.click) {
                info.click(pos, posf);
            }
        });
    }

    public mousemove() {
        const { Mota, pos, posf } = this
        Mota.ForChildren((info) => {
            if (info.mousemove) {
                info.mousemove(pos, posf);
            }
        });
    }

    public mousedown() {
        const { Mota, pos, posf } = this
        Mota.ForChildren((info) => {
            if (info.mousedown) {
                info.mousedown(pos, posf);
            }
        });
    }

    public mouseup() {
        const { Mota, pos, posf } = this
        Mota.ForChildren((info) => {
            if (info.mouseup) {
                info.mouseup(pos, posf);
            }
        });
    }

    public mousewheel(delta: number) {
        const { Mota } = this
        Mota.ForChildren((info) => {
            if (info.mousewheel) {
                info.mousewheel(delta);
            }
        });
    }
}